
#include "Model.h"
#include <iostream>


Model::Model(const char* obj_file) :

m_diffuseTexture(NULL),
m_paintedTexture(NULL)

{
	matTotal = 0;		// mat[0] reserved for default meterial

	 // default material: mat[0]
	 mat[0].Ka[0] = 0.0; mat[0].Ka[1] = 0.0; mat[0].Ka[2] = 0.0; mat[0].Ka[3] = 1.0; 
	 mat[0].Kd[0] = 1.0; mat[0].Kd[1] = 1.0; mat[0].Kd[2] = 1.0; mat[0].Kd[3] = 1.0; 
	 mat[0].Ks[0] = 0.8; mat[0].Ks[1] = 0.8; mat[0].Ks[2] = 0.8; mat[0].Ks[3] = 1.0;
	 mat[0].Ns = 32;
	 matTotal++;

	bool result = LoadObj(string(obj_file));	
	if (!result) 
	{
		printf("Error loading model: %s\n", obj_file);
		exit(EXIT_FAILURE);
	}
		
}

Model::~Model()
{
}



bool Model::LoadObj(string obj_file)
{
	float scale = 1.0f;
	
	Geometry geometry;
	
	char	token[1024], buf[1024];	

	int	n_vertex, n_texture, n_normal;
	int	cur_tex = 0;				// state variable

	fp_scene = fopen(obj_file.c_str(),"r");
	s_file = obj_file;

	if (!fp_scene) 
	{
		cout<< string("Cannot open object File \"") << obj_file << "\" !" << endl;
		return -1;
	}

	cout << endl << obj_file << endl;
		
	while( !feof(fp_scene) )
	{
		token[0] = '\0';
		fscanf(fp_scene, "%s", token);		

		if (!strcmp(token,"g"))
		{
			fscanf(fp_scene,"%s",buf);
		}

		else if (!strcmp(token,"mtllib"))
		{
  			fscanf(fp_scene,"%s", mat_file);
			LoadObjMaterial(string(mat_file));
		}

		else if (!strcmp(token,"usemtl"))
		{
			fscanf(fp_scene,"%s",buf);
			cur_tex = matMap[s_file+string("_")+string(buf)];
		}

		else if (!strcmp(token,"v"))
		{
			Vec4 pos;
			fscanf(fp_scene, "%f %f %f", &pos[0], &pos[1], &pos[2]);
			pos *= scale;
			geometry.m_vertices.push_back(pos);
		}

		else if (!strcmp(token,"vn"))
		{
			Vec4 norm;
			fscanf(fp_scene,"%f %f %f", &norm[0], &norm[1], &norm[2]);
			norm.normalizeIfNotZero();
			geometry.m_vertexNormals.push_back(norm);
		}
		else if (!strcmp(token,"vt"))
		{
			Vec4 UV;
			fscanf(fp_scene, "%f %f", &UV[0], &UV[1]);
			geometry.m_UVs.push_back(UV);
		}

		else if (!strcmp(token,"f"))
		{
			// read buffer up to newline
			char line[1024];
			char* p = line;
			fgets(p, 1024, fp_scene);
			
			// read vertex indices
			int faceVertex = 0;
			
			const int MAX_FACE_VERTICES = 256;
			int vertexIndices[MAX_FACE_VERTICES];
			int normalIndices[MAX_FACE_VERTICES];
			int uvIndices[MAX_FACE_VERTICES];
			
			do
			{
				char faceEntry[64];
				
				// skip any spaces
				while ( *p == ' ' ) p++;
				
				sscanf(p, "%s", faceEntry);
				
				if ( *p < ' ' ) break;
				
				char str[20], ch;
				int base,offset;
				base = offset = 0;
				
				// calculate vertex-list index
				while( (ch=faceEntry[base+offset]) != '/' && (ch=faceEntry[base+offset]) != '\0')
				{
					str[offset] = ch;
					offset++;
				}
				str[offset] = '\0';
				n_vertex = atoi(str);
				vertexIndices[faceVertex] = n_vertex-1; // -1 because indices in .obj are 1-based
				
				base += (ch == '\0')? offset : offset+1;
				offset = 0;
				
				// calculate texture-list index
				bool haveUV = false;
				while( (ch=faceEntry[base+offset]) != '/' && (ch=faceEntry[base+offset]) != '\0')
				{
					haveUV = true;
					str[offset] = ch;
					offset++;
				}
				str[offset] = '\0';
				
				if (haveUV)
				{
					n_texture = atoi(str);	
					uvIndices[faceVertex] = n_texture-1; // -1 because indices in .obj are 1-based
				}
				else
				{
					uvIndices[faceVertex] = -1;
				}
				
				base += (ch == '\0')? offset : offset+1;
				offset = 0;
				
				// calculate normal-list index
				bool haveNormal = false;
				while( (ch=faceEntry[base+offset]) != '\0')
				{
					haveNormal = true;
					str[offset] = ch;
					offset++;
				}
				str[offset] = '\0';
				
				if (haveNormal)
				{
					n_normal = atoi(str);	
					normalIndices[faceVertex] = n_normal-1; // -1 because indices in .obj are 1-based
				}
				else
				{
					normalIndices[faceVertex] = -1;
				}
				
				// Move line along to next space
				p = strchr(p, ' ');
				
				faceVertex++;
				
			} while (p && faceVertex < MAX_FACE_VERTICES);
			
			
			// Triangulate face - assuming vertices in order
			int numFaceVertices = faceVertex;
			
			for (int i2 = 2; i2 < numFaceVertices; i2 ++)
			{
				Geometry::Triangle triangle;
				
				triangle.m_vertex[0] = vertexIndices[0];
				triangle.m_vertex[1] = vertexIndices[i2-1];
				triangle.m_vertex[2] = vertexIndices[i2];
				
				if ( uvIndices[0] >= 0 && uvIndices[i2-1] >= 0 && uvIndices[i2] >= 0 )
				{
					triangle.m_uvs[0] = uvIndices[0];
					triangle.m_uvs[1] = uvIndices[i2-1];
					triangle.m_uvs[2] = uvIndices[i2];
				}
				
				if ( normalIndices[0] >= 0 && normalIndices[i2-1] >= 0 && normalIndices[i2] >= 0 ) 
				{
					triangle.m_vertexNormal[0] = normalIndices[0];
					triangle.m_vertexNormal[1] = normalIndices[i2-1];
					triangle.m_vertexNormal[2] = normalIndices[i2];
				}
				
				geometry.m_triangles.push_back(triangle);
			}
		}
		
		else if (!strcmp(token,"#"))
		{
			fgets(buf, 100, fp_scene);
		}
	}

	if (fp_scene) fclose(fp_scene);

	m_geometry = geometry;
	
	// Move model and uniformly rescale to make a standard size (make maximum AABB dimension 100). 
	// The same normals will be fine on the uniformly rescaled model.
	Aabb aabb = m_geometry.computeAabb();
	Vec4 center = 0.5f * (aabb.m_minima + aabb.m_maxima);
	
	Vec4 extents = aabb.m_maxima - aabb.m_minima;
	
	float maxExtent;
	extents.maxIndex(maxExtent);
	
	float scaleFactor = 100.f/maxExtent;
	
	for (int vi=0; vi<m_geometry.m_vertices.size(); ++vi)
	{
		Vec4& P = m_geometry.m_vertices[vi];
		P -= center;
		P *= scaleFactor;
	}
	
	// Precompute these
	m_geometry.computeSmoothedNormals();
	
	return true;
}



void Model::loadDiffuseTexture(std::string filename)
{
	m_diffuseTexture = new Texture();
	m_diffuseTexture->Load(filename);
}


void Model::loadPaintedTexture(std::string filename)
{
	m_paintedTexture = new Texture();
	m_paintedTexture->Load(filename);
}



bool Model::LoadObjMaterial(string mat_file)
{
	char	token[100], buf[100];
	float	r,g,b;

	fp_material = fopen(mat_file.c_str(), "r");
	t_file = mat_file;

	if (!fp_material) 
	{
		cout << "Can't open material file \"" << mat_file << "\"!" << endl;
		return false;
	}

	cout << mat_file << endl;

	int cur_mat = 0;

	while(!feof(fp_material))
	{
		token[0] = '\0';
		fscanf(fp_material,"%s", token);	
		if (!strcmp(token,"newmtl"))
		{
			fscanf(fp_material,"%s",buf);
			cur_mat = matTotal++;					
			matMap[s_file+string("_")+string(buf)] = cur_mat; 	// matMap["material_name"] = material_id;
		}

		else if (!strcmp(token,"Ka"))
		{
			fscanf(fp_material,"%f %f %f",&r,&g,&b);
			mat[cur_mat].Ka[0] = r;
			mat[cur_mat].Ka[1] = g;
			mat[cur_mat].Ka[2] = b;
			mat[cur_mat].Ka[3] = 1;
		}

		else if (!strcmp(token,"Kd"))
		{
			fscanf(fp_material,"%f %f %f",&r,&g,&b);
			mat[cur_mat].Kd[0] = r;
			mat[cur_mat].Kd[1] = g;
			mat[cur_mat].Kd[2] = b;
			mat[cur_mat].Kd[3] = 1;
		}

		else if (!strcmp(token,"Ks"))
		{
			fscanf(fp_material,"%f %f %f",&r,&g,&b);
			mat[cur_mat].Ks[0] = r;
			mat[cur_mat].Ks[1] = g;
			mat[cur_mat].Ks[2] = b;
			mat[cur_mat].Ks[3] = 1;
		}

		else if (!strcmp(token,"Ns"))
		{
			fscanf(fp_material,"%f",&r);
			mat[cur_mat].Ns = r;
		}

		else if (!strcmp(token,"#"))	
			fgets(buf,100,fp_material);

	}

	printf("total material:%d\n", (int)matMap.size());

	if (fp_material) fclose(fp_material);
	
	return true;
}


void Model::debugRender()
{
	glColor4f(1,1,1,1);
	
	glDisable(GL_TEXTURE_2D);
	//if (m_diffuseTexture)
	//	m_diffuseTexture->Apply();
	
	m_geometry.draw(false);
}





